package com.gksc.video.manager;

import com.gksc.video.config.PushConfig;
import com.gksc.video.constant.BusinessConstant;
import com.gksc.video.data.DeviceInfo;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;

import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;

/**
 * @className: BasePushThread
 * @description: 基础推流线程信息类
 * @author: Liao.yx
 * @email: 1229053515@qq.com
 * @create: 2021年05月19日 16时27分
 * @copyRight: 2020 liaoyongxiang All rights reserved.
 **/
@Slf4j
public class BasePushThread {
    /**
     * 退出状态码：0-正常退出;1-手动中断
     */
    private Integer exitCode = BusinessConstant.EXIT_SYSTEM;
    /**
     * 推流过程中出现错误的次数
     */
    private int errorNum = 0;
    /**
     * 帧率
     */
    private double frameRate = 0.0;
    /**
     * 解码器
     */
    private FFmpegFrameRecorder recorder;

    /**
     * 取消线程
     */
    public void cancel() {
        exitCode = BusinessConstant.EXIT_HANDLER;
    }

    public void release() throws Exception{
        if(recorder != null){
            recorder.stop();
            recorder.release();
        }
    }

    /**
     * 推流
     * @param grabber 采集器
     * @param device 设备信息
     * @throws Exception
     */
    public void push(FFmpegFrameGrabber grabber, DeviceInfo device) throws Exception {
        if (grabber.getFrameRate() > 0 && grabber.getFrameRate() < 100) {
            frameRate = grabber.getFrameRate();
        }else{
            frameRate = 25.0;
        }
        int width = grabber.getImageWidth();
        int height = grabber.getImageHeight();
        // 若视频像素值为0，说明拉流异常，程序结束
        if (width == 0 && height == 0) {
            log.error("设备{}拉流异常！", device.toString());
            return;
        }
        recorder = new FFmpegFrameRecorder(device.getRtmpUrl(), width, height);
        recorder.setInterleaved(true);
        recorder.setGopSize((int) frameRate * 2);// 关键帧间隔，一般与帧率相同或者是视频帧率的两倍
        recorder.setFrameRate(frameRate); // 视频帧率(保证视频质量的情况下最低25，低于25会出现闪屏)
        recorder.setVideoBitrate(grabber.getVideoBitrate());// 设置比特率
        recorder.setFormat(BusinessConstant.PUSH_FORMAT); // 封装flv格式
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// h264编/解码器
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);//设置音频编码
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setOption(BusinessConstant.RecorderOption.FLV_FLAGS, "no_duration_filesize");
        recorder.setVideoOption(BusinessConstant.RecorderOption.ENCODE_DELAY, "zerolatency");
        recorder.setVideoOption(BusinessConstant.RecorderOption.ENCODE_SPEED, "ultrafast");
        recorder.setAudioOption(BusinessConstant.RecorderOption.IMAGE_QUALITY, "0");
        recorder.setVideoOption(BusinessConstant.RecorderOption.IMAGE_QUALITY, "28");
        AVFormatContext fc = grabber.getFormatContext();
        recorder.start(fc);
        log.info("开始推流 设备信息：{}", device.toString());
        // 清空探测时留下的缓存
        grabber.flush();

        AVPacket pkt = null;
        long dts = 0;
        long pts = 0;
        int timebase = 0;
        for (int noFrameIdx = 0; noFrameIdx < 5 && errorNum < 5; ) {
            long time = System.currentTimeMillis();
            if (exitCode.equals(BusinessConstant.EXIT_HANDLER)) {
                break;
            }
            pkt = grabber.grabPacket();
            if (pkt == null || pkt.size() == 0 || pkt.data() == null) {
                // 空包记录次数跳过
                log.error("视频解析出现空包，设备信息：{}", device.toString());
                noFrameIdx++;
                continue;
            }
            // 过滤音频
            if (pkt.stream_index() == 1) {
                av_packet_unref(pkt);
                continue;
            }
            // 矫正sdk回调数据的dts，pts每次不从0开始累加所导致的播放器无法续播问题
            pkt.pts(pts);
            pkt.dts(dts);
            errorNum += (recorder.recordPacket(pkt) ? 0 : 1);
            // pts,dts累加
            timebase = grabber.getFormatContext().streams(pkt.stream_index()).time_base().den();
            pts += timebase / (int) frameRate;
            dts += timebase / (int) frameRate;
            // 将缓存空间的引用计数-1，并将Packet中的其他字段设为初始值。如果引用计数为0，自动的释放缓存空间。
            av_packet_unref(pkt);

            long endTime = System.currentTimeMillis();
            if ((long) (1000 / frameRate) - (endTime - time) > 0) {
                Thread.sleep((long) (1000 / frameRate) - (endTime - time));
            }
        }
    }
}
